热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

池子比率:BSV区块链上的去中心化金融应用——Uniswap分析

篇首语:本文由编程笔记#小编为大家整理,主要介绍了BSV 区块链上的 DeFi :Uniswap相关的知识,希望对你有一定的参考价值。 Uniswap Uniswap 是所谓的 Decentrali

篇首语:本文由编程笔记#小编为大家整理,主要介绍了BSV 区块链上的 DeFi :Uniswap相关的知识,希望对你有一定的参考价值。



Uniswap

Uniswap 是所谓的 Decentralized Exchange,它允许个人或称为流动性提供者,将 Token 汇集到智能合约中提供流动性。


概述

我们实现了 Uniswap V1,它只在 BSV 和 Token 之间直接交换。如@state 装饰器所示,我们使用一个带有 @state 装饰器 的有状态合约来表示池子。它包含两个 Token :一个用于我们正在交换的 Token (第 7 行),另一个是治理 Token (第 11 行),称为流动性池(LP) Token 。该池将 BSV 直接存储在 UTXO 中(以 satoshis 为单位),将 Token 存储在对应的公钥哈希下(第 3 行)。

contract Uniswap
// pool's public key
PubKey poolPubkey;
// the main token
@state
ERC20 token;
// the liquidity pool governance token
@state
ERC20 lpToken;
...



Uniswap 合约源代码

增加流动性

任何人都可以通过调用函数 addLiquidity 向池中添加流动性。有两种情况(在第 9 行检查):


  1. 首次增加流动性:可以存入任意数量的 BSV 和 Token 。
  2. 添加更多流动性: BSV 和 Token 存入的比率必须与池中的现有比率相匹配(第 22 行)。

// add bsv and token to liquidity pool
public function addLiquidity(PubKey sender, Sig senderSig, int tokenAmount, int senderBalance, int senderKeyIndex, int oldTokenBalance,
int lpSenderBalance, int lpSenderKeyIndex, int newBsvBalance, SigHashPreimage txPreimage
)
require(checkSig(senderSig, sender));
int oldBsvBalance = SigHash.value(txPreimage);

// mint new lp tokens for the liquidity provider
if (oldBsvBalance == 0)
// initialize pool

// initially, just mint new lp tokens per the amount of new bsvs deposited
int lpMint = newBsvBalance;
require(this.lpToken.mint(sender, lpSenderBalance, lpMint, lpSenderKeyIndex));
else
// add more liquidity
int bsvAmount = newBsvBalance - oldBsvBalance;

// deposit ratio must be the same with current pool ration
// i.e., oldBsvBalance / oldTokenBalance == bsvAmount / tokenAmount
require(oldBsvBalance * tokenAmount == bsvAmount * oldTokenBalance);
// mint new lp tokens, proportinal to the amount of new bsvs deposited
int lpMint = this.lpToken.totalSupply() * bsvAmount / oldBsvBalance;
require(this.lpToken.mint(sender, lpSenderBalance, lpMint, lpSenderKeyIndex));

// transfer tokens to the pool
require(this.token.transferFrom(sender, this.poolPubkey, tokenAmount, senderBalance, senderKeyIndex, oldTokenBalance, senderKeyIndex));

require(this.propagateState(newBsvBalance, txPreimage));

存入 BSV 后,新的 LP Token 在第 26 行按比例铸造给流动性提供者。 Token 在第 30 行转移到池子对应的账户。

例如,如果池中有 10 个 BSV 和 100 个 LP Token ,而 Alice 又向其中存入了 5 个 BSV ,则将向她铸造 50 个新的 LP Token 。


移除流动性

流动性提供者调用函数 removeLiquidity 来提取他们的资金,包括 BSV 和 Token 。

// remove bsv and token from liquidity pool
public function removeLiquidity(PubKey sender, int lpAmount, Sig senderSig, int oldTokenBalance, int senderKeyIndex, int senderBalance,
int lpSenderBalance, int lpSenderKeyIndex, SigHashPreimage txPreimage
)
require(checkSig(senderSig, sender));
int oldBsvBalance = SigHash.value(txPreimage);

// withdraw amount
int bsvAmount = oldBsvBalance * lpAmount / this.lpToken.totalSupply();
int tokenAmount = oldTokenBalance * lpAmount / this.lpToken.totalSupply();
// burn the lp tokens
require(this.lpToken.burn(sender, lpSenderBalance, lpAmount, lpSenderKeyIndex));
// transfer tokens from pool to the sender
require(this.token.transferFrom(this.poolPubkey, sender, tokenAmount, oldTokenBalance, senderKeyIndex, senderBalance, senderKeyIndex));
// transfer bsvs to the sender
int newBsvBalance = oldBsvBalance - bsvAmount;

require(this.propagateState(newBsvBalance, txPreimage));

流动性提供者拥有的 LP Token 的数量来提取流动性(第 9 行和第 10 行)。提款后,LP Token 在第 13 行被烧毁。第 16 行将 Token 从池子中转移到流动性提供者。第 18 行和第 20 行对 BSV 做同样的事情。

请注意,除了合约输出之外,还需要在同一交易中的另一个输出将 BSV 返回给流动性提供者。


BSV -> Token

用户调用函数 swapBsvToToken 将 BSV 兑换成 Token 。在池子收到 BSV 后(在第 7 行计算),第 10 行计算要返回的 Token 数量,第 13 行将它们返回给用户。

// swap bsvs for tokens
public function swapBsvToToken(PubKey sender, int tokenAmount, Sig senderSig, int oldTokenBalance, int senderKeyIndex, int senderBalance,
int newBsvBalance, SigHashPreimage txPreimage
)
require(checkSig(senderSig, sender));
int oldBsvBalance = SigHash.value(txPreimage);
int bsvAmount = newBsvBalance - oldBsvBalance;
// calculate tokens in return
int tokensAmount = this.getAmount(bsvAmount, oldBsvBalance, oldTokenBalance);
// transfer tokens from pool to the sender
require(this.token.transferFrom(this.poolPubkey, sender, tokensAmount, oldTokenBalance, senderKeyIndex, senderBalance, senderKeyIndex));
require(this.propagateState(newBsvBalance, txPreimage));


Token -> BSV

用户调用函数 swapTokenToBsv 将 Token 兑换成 BSV 。第 9 行计算要返回的 BSV 数量。第 13 行将 Token 转移到池子中。

// swap tokens for bsvs
public function swapTokenToBsv(PubKey sender, int tokenAmount, Sig senderSig, int senderBalance, int senderKeyIndex, int oldTokenBalance,
int lpSenderBalance, SigHashPreimage txPreimage
)
require(checkSig(senderSig, sender));
int oldBsvBalance = SigHash.value(txPreimage);
// calculate bsvs in return
int bsvsAmount = this.getAmount(tokenAmount, oldTokenBalance, oldBsvBalance);
int newBsvBalance = oldBsvBalance - bsvsAmount;
// transfer tokens to the pool
require(this.token.transferFrom(sender, this.poolPubkey, tokenAmount, senderBalance, senderKeyIndex, oldTokenBalance, senderKeyIndex));

require(this.propagateState(newBsvBalance, txPreimage));

removeLiquidity 类似,需要另一个输出来将 BSV 返回给用户。


讨论

我们已经演示了如何在 BSV 区块链上实现一个基本的类似 Uniswap 的合约。有很多方法可以扩展它以使其更实用。


  • 价格公式:我们使用以下代码来确定价格,仅基于 BSV 和 Token 储备。它被称为恒定和公式,可能导致池子被排空。为了避免耗尽,可以使用更复杂的公式,如恒定乘积公式 (x * y = k),如 Uniswap 中。

// use reserve ratio as price
function getAmount(int input, int inputReserve, int outputReserve) : int
return outputReserve * input / inputReserve;


  • 流动性挖矿:我们可以对每次交换收取费用,并用这些费用来奖励流动性提供者。
  • 允许用户直接将一个 Token 换成另一个 Token 。

TokenSwap 实际上已经实现了以上所有以及更多。


致谢

本作品灵感来源于陈诚的这篇文章。


推荐阅读
  • 本文详细介绍了如何在Linux系统上安装和配置Smokeping,以实现对网络链路质量的实时监控。通过详细的步骤和必要的依赖包安装,确保用户能够顺利完成部署并优化其网络性能监控。 ... [详细]
  • 本文将介绍如何编写一些有趣的VBScript脚本,这些脚本可以在朋友之间进行无害的恶作剧。通过简单的代码示例,帮助您了解VBScript的基本语法和功能。 ... [详细]
  • 1.如何在运行状态查看源代码?查看函数的源代码,我们通常会使用IDE来完成。比如在PyCharm中,你可以Ctrl+鼠标点击进入函数的源代码。那如果没有IDE呢?当我们想使用一个函 ... [详细]
  • 本文详细介绍了Akka中的BackoffSupervisor机制,探讨其在处理持久化失败和Actor重启时的应用。通过具体示例,展示了如何配置和使用BackoffSupervisor以实现更细粒度的异常处理。 ... [详细]
  • 本文介绍如何使用Objective-C结合dispatch库进行并发编程,以提高素数计数任务的效率。通过对比纯C代码与引入并发机制后的代码,展示dispatch库的强大功能。 ... [详细]
  • 深入理解 SQL 视图、存储过程与事务
    本文详细介绍了SQL中的视图、存储过程和事务的概念及应用。视图为用户提供了一种灵活的数据查询方式,存储过程则封装了复杂的SQL逻辑,而事务确保了数据库操作的完整性和一致性。 ... [详细]
  • 本文详细介绍了 Dockerfile 的编写方法及其在网络配置中的应用,涵盖基础指令、镜像构建与发布流程,并深入探讨了 Docker 的默认网络、容器互联及自定义网络的实现。 ... [详细]
  • 数据库内核开发入门 | 搭建研发环境的初步指南
    本课程将带你从零开始,逐步掌握数据库内核开发的基础知识和实践技能,重点介绍如何搭建OceanBase的开发环境。 ... [详细]
  • 本文详细介绍了如何使用 Yii2 的 GridView 组件在列表页面实现数据的直接编辑功能。通过具体的代码示例和步骤,帮助开发者快速掌握这一实用技巧。 ... [详细]
  • 在前两篇文章中,我们探讨了 ControllerDescriptor 和 ActionDescriptor 这两个描述对象,分别对应控制器和操作方法。本文将基于 MVC3 源码进一步分析 ParameterDescriptor,即用于描述 Action 方法参数的对象,并详细介绍其工作原理。 ... [详细]
  • 本文详细介绍如何使用Python进行配置文件的读写操作,涵盖常见的配置文件格式(如INI、JSON、TOML和YAML),并提供具体的代码示例。 ... [详细]
  • 本文介绍了如何使用 Spring Boot DevTools 实现应用程序在开发过程中自动重启。这一特性显著提高了开发效率,特别是在集成开发环境(IDE)中工作时,能够提供快速的反馈循环。默认情况下,DevTools 会监控类路径上的文件变化,并根据需要触发应用重启。 ... [详细]
  • Java 类成员初始化顺序与数组创建
    本文探讨了Java中类成员的初始化顺序、静态引入、可变参数以及finalize方法的应用。通过具体的代码示例,详细解释了这些概念及其在实际编程中的使用。 ... [详细]
  • 前言--页数多了以后需要指定到某一页(只做了功能,样式没有细调)html ... [详细]
  • DNN Community 和 Professional 版本的主要差异
    本文详细解析了 DotNetNuke (DNN) 的两种主要版本:Community 和 Professional。通过对比两者的功能和附加组件,帮助用户选择最适合其需求的版本。 ... [详细]
author-avatar
爱旅游的星琴
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有